'''
A job that executes a command line and logs it's output.

Licensed under the GPL.

@author: screwdriver
'''

'''
Version history:
----------------
2009-03-27 screwdirver Created.
'''

import io;
import subprocess;
import string;

from debug import debug;

from threading import Thread;
from job import Job;
from string import Template;
from subprocess import Popen;
from subprocess import PIPE;


MAJOR_VERSION = 1;
MINOR_VERSION = 0;
MAINTENANCE = 0;

VERSION = [MAJOR_VERSION,".", MINOR_VERSION,".", MAINTENANCE];

COMMAND_LINE_TOOL_ERROR = "The process returned with error-code $code";


class CommandLine(object):
	'''
	Model for command lines of the pattern <tool> <options> <arguments>
	'''
	
	def tool(): #@NoSelf
		doc = """The tool to call.""" #@UnusedVariable
		
		def fget(self):
			return self._tool
			
		def fset(self, value):
			self._tool = value
			
		def fdel(self):
			del self._tool
			
		return locals()
		
	tool = property(**tool());
	
	def options(): #@NoSelf
		doc = """The list of Options. A list of Option instances.""" #@UnusedVariable
		
		def fget(self):
			return self._options
			
		def fset(self, value):
			self._options = value
			
		def fdel(self):
			del self._options
			
		return locals()
		
	options = property(**options());
	
	def arguments(): #@NoSelf
		doc = """The list of arguments. A list of strings.""" #@UnusedVariable
		
		def fget(self):
			return self._arguments
			
		def fset(self, value):
			self._arguments = value
			
		def fdel(self):
			del self._arguments
			
		return locals()
		
	arguments = property(**arguments())
	
	def __init__(self, tool):
		self.tool = tool;
		self.options = [];
		self.arguments = [];
		
	def __str__(self):
		commandLine = self.tool;
		
		for option in self.options:
			commandLine += " " + str(option);
		
		commandLine += " " + string.join(self.arguments, " ");
		
		return commandLine; 
		
		
class Option(object):
	'''
	Command line option of pattern:
	<prefix><name><separator><value>
	There are several prefixes and separators defined, you can add any string though.
	
	Examples:
	"-p": prefix="-", name="p"
	"--output=/home/output.file": prefix="--", name="output", separator="=", value="/home/output.file"
	'''
	
	PREFIX_DASH_LONG = "--";
	PREFIX_DASH_SHORT = "-";
	PREFIX_SLASH = "/";
	
	SEPARATOR_SPACE = " ";
	SEPARATOR_EQUALS = "=";
	
	def prefix(): #@NoSelf
		doc = """String used to mark options.""" #@UnusedVariable
		
		def fget(self):
			return self._prefix
			
		def fset(self, value):
			self._prefix = value
			
		def fdel(self):
			del self._prefix
			
		return locals()
		
	prefix = property(**prefix());
	
	def name(): #@NoSelf
		doc = """Name of the option.""" #@UnusedVariable
		
		def fget(self):
			return self._name
			
		def fset(self, value):
			self._name = value
			
		def fdel(self):
			del self._name
			
		return locals()
		
	name = property(**name());
	
	def separator(): #@NoSelf
		doc = """String used to separate option from it's value""" #@UnusedVariable
		
		def fget(self):
			return self._separator
			
		def fset(self, value):
			self._separator = value
			
		def fdel(self):
			del self._separator
			
		return locals()
		
	separator = property(**separator());
	
	def value(): #@NoSelf
		doc = """Value of the option, can be empty.""" #@UnusedVariable
		
		def fget(self):
			return self._value
			
		def fset(self, value):
			self._value = value
			
		def fdel(self):
			del self._value
			
		return locals()
		
	value = property(**value());
	
	def __init__(self, name, value):
		self.prefix = Option.PREFIX_DASH_LONG;
		self.name = name;
		self.separator = Option.SEPARATOR_SPACE;
		self.value = value;	
	
	def __str__(self):
		return self.prefix + self.name + self.separator + self.value;

class ExcecuteCommand(Job):
	'''
	Excecute a command line in a thread
	'''
	
	def commandLine(): #@NoSelf
		doc = """The command line to execute.""" #@UnusedVariable
		
		def fget(self):
			return self._commandLine
			
		def fset(self, value):
			self._commandLine = value
			
		def fdel(self):
			del self._commandLine
			
		return locals()
		
	commandLine = property(**commandLine());
	
	def logFileName(): #@NoSelf
		doc = """Name for the two log files. The two will be called <logFileName>.err.log and <logFileName>.out.log.""" #@UnusedVariable
		
		def fget(self):
			return self._logFileName
			
		def fset(self, value):
			self._logFileName = value
			
		def fdel(self):
			del self._logFileName
			
		return locals()
		
	logFileName = property(**logFileName());
	
	def __outputLog(): #@NoSelf
		doc = """Output stream for stdout""" #@UnusedVariable
		
		def fget(self):
			return self.___outputLog
			
		def fset(self, value):
			self.___outputLog = value
			
		def fdel(self):
			del self.___outputLog
			
		return locals()
		
	__outputLog = property(**__outputLog())
	
	def ___errorLog(): #@NoSelf
		doc = """Output stream for stderr""" #@UnusedVariable
		
		def fget(self):
			return self.____errorLog
			
		def fset(self, value):
			self.____errorLog = value
			
		def fdel(self):
			del self.____errorLog
			
		return locals()
		
	___errorLog = property(**___errorLog())
	
	def __init__(self, commandLine, logFileName):
		Job.__init__(self);
		self.commandLine = commandLine;
		self.logFileName = logFileName;
		
	def initialize(self):
		self.__outputLog = io.TextIOWrapper(io.BufferedWriter(io.FileIO(self.logFileName + ".out.log", "w")));
		self.__errorLog = io.TextIOWrapper(io.BufferedWriter(io.FileIO(self.logFileName + ".err.log", "w")));
	
	def execute(self):
		try:

			process = subprocess.Popen(self.commandLine, stdout=PIPE, stderr=PIPE);
			
			(stdoutdata, stderrdata) = process.communicate();
			
			stdoutdata = unicode(stdoutdata);
			stderrdata = unicode(stderrdata);
			
			self.__outputLog.write(Template("-------------------------------------\n$command\n").safe_substitute(command = unicode(self.commandLine)));
			self.__outputLog.write(stdoutdata);
			self.__outputLog.flush();
			
			self.__errorLog.write(Template("-------------------------------------\n$command\n").safe_substitute(command = unicode(self.commandLine)));
			self.__errorLog.write(stderrdata);
			self.__errorLog.flush();
			
			if process.returncode != 0:
				self.successful = False;
				error = unicode(Template(COMMAND_LINE_TOOL_ERROR).safe_substitute(code =process.returncode));
				self.lasterror = error;
				self.__errorLog.write(error);
				self.__errorLog.flush();
			else:
				self.successful = True;
				
		except Exception, e:
			self.successful = False;
			self.lasterror = repr(e);
	
	def cleanup(self):
		self.__outputLog.close();
		self.__errorLog.close();
	
	def run(self):
		self.initialize();
		self.execute();
		self.cleanup();
		